﻿

/*
 * reference this for in-place editable improvement http://blog.ryanvanderpol.com/index.php/2012/07/using-knockoutjs-to-create-an-edit-in-place-data-grid/
 *
 * For template sources: http://www.knockmeout.net/2011/10/ko-13-preview-part-3-template-sources.html
 */


ko.bindingHandlers.grid = {
    init: function (element, valueAccessor, allBindingsAccessor, viewModel) {
        var $el = $(element);
        var options = allBindingsAccessor().gridOptions;
        var model = new IDataGrid(valueAccessor(), options);
        if (typeof options.rowClick === 'function')
            model.rowClick = options.rowClick;

        //$el.find('thead > tr:first > th').each(function () {
        //    viewModel.columns.push(new IColumn($(this).data('field')));
        //});
        var $table = model.buildTable();
        $el.append($table);

        ko.applyBindings(model, $table[0]);
        //ko.applyBindings(model, $navigator[0]);

        // this binding will control its all children bindings.
        return { 'controlsDescendantBindings': true };
    }
};

ko.bindingHandlers.row = {
    makeTemplate: function (valueAccessor) {
        return function() {
            return { 'foreach': valueAccessor(), 'templateEngine': ko.nativeTemplateEngine.instance }
        };
    },
    init: function (element, valueAccessor, allBindingsAccessor, viewModel, context) {
        $(element).click(function (event) {
            context.$root.editingRow(context.$index());
            
            //viewModel.isActive(true);
            //cell(cell() + 'a');
            //return ko.bindingHandlers.column.update(element, valueAccessor, allBindingsAccessor, viewModel, context);
            //event.preventDefault();
        });
        //viewModel.isActive = ko.observable(false);
        return ko.bindingHandlers.template.init(element, ko.bindingHandlers.row.makeTemplate(valueAccessor), allBindingsAccessor, viewModel, context);
    },
    update: function (element, valueAccessor, allBindingsAccessor, viewModel, context) {
        return ko.bindingHandlers.template.update(element, ko.bindingHandlers.row.makeTemplate(valueAccessor), allBindingsAccessor, viewModel, context);
    }
};
ko.bindingHandlers.column = {
    makeTemplate: function (valueAccessor) {
        return function () {
            return { 'templateEngine': this.templateEngine, 'name': ko.bindingHandlers.column.template }
        };
    },
    template: function(viewModel, bindingContext) {
        return bindingContext.$root.editingRow() == bindingContext.$parentContext.$index() ? 'edit' :
            'view';
    },
    init: function (element, valueAccessor, allBindingsAccessor, viewModel, context) {
        var self = this;
        
        //$(element).click(function (event) {
        //    context.$root.editingRow(context.$parentContext.$index());
        //    //cell(cell() + 'a');
        //    //return ko.bindingHandlers.column.update(element, valueAccessor, allBindingsAccessor, viewModel, context);
        //    //event.preventDefault();
        //});
        this.templateEngine = viewModel.templateEngine;
        return ko.bindingHandlers.template.init(element, ko.bindingHandlers.column.makeTemplate(valueAccessor), allBindingsAccessor, viewModel, context);
    },
    update: function (element, valueAccessor, allBindingsAccessor, viewModel, context) {
            var cell = $(element);
            
            //if (cell.hasClass('editing')) {
                
            //} else {
        //cell.text(ko.unwrap(valueAccessor()));
            //var templateComputed = viewModel.templateEngine.renderTemplate(element, context, {}, null);
            //disposeOldComputedAndStoreNewOne(element, templateComputed);
        //}
            //cell.text('Hey');
           
            return ko.bindingHandlers.template.update(element, ko.bindingHandlers.column.makeTemplate(valueAccessor), allBindingsAccessor, viewModel, context);
            //return ko.bindingHandlers.template.update(element, valueAccessor, allBindingsAccessor, viewModel, context);
            //return ko.bindingHandlers.template.update(element, function () { return options; }, allBindingsAccessor, viewModel, context);
            //return ko.bindingHandlers['template']['update'](element, ko.bindingHandlers['foreach'].makeTemplateValueAccessor(valueAccessor));
        }
};

ko.templateSources.columnTemplate = function (template, model, data) {
    this.domElement = template;

    this.model = model;
    this.text = function () {
        if (arguments.length == 0) {
            var cell = $(this.domElement);
            if (template == 'edit') {
                switch (model.columnType) {
                    case 0: /* Text */ return '<input type="text" class="form-control" data-bind="value: $parent[$data.field]">';
                    case 1: /* Date */ return '<input type="text" class="form-control" data-bind="datepicker: $parent[$data.field], datepickerOptions: {format: \'' + model.options.dateFormat +'\' }">';
                }
                
            }
            else {
                switch (model.columnType) {
                    case 0: return '<span data-bind="text: $parent[$data.field]">';
                    case 1: return '<span data-bind="text: moment($parent[$data.field]()).format(\'' + model.options.dateFormat + '\')" >';
                    
                }
            }
                
            //var templateData = ko.utils.domData.get(this.domElement, anonymousTemplatesDomDataKey) || {};
            //if (templateData.textData === undefined && templateData.containerData)
            //    templateData.textData = templateData.containerData.innerHTML;
            //return templateData.textData;
        } else {
            var valueToWrite = arguments[0];
            ko.utils.domData.set(this.domElement, anonymousTemplatesDomDataKey, { textData: valueToWrite });
        }
        
    };
    //this.nodes = function(/* valueToWrite */) {
    //    if (arguments.length == 0) {
    //        var cell = $(this.domElement);
    //        var $div = $('<div>');
    //        //if (cell.hasClass("editing")) {
    //            $div.append($('<input data-bind="value: $parent[$data.field]">')[0]);
    //        //} else {
    //            //$div.append($('<span data-bind="text: $parent[$data.field]">')[0]);
    //        //}
            
    //        return $div[0];
    //    } else {
    //        var valueToWrite = arguments[0];
    //        ko.utils.domData.set(this.domElement, anonymousTemplatesDomDataKey, {containerData: valueToWrite});
    //    }
    //};
    //return new ko.templateSources.anonymousTemplate(template);
};

function IDataGrid(data, options) {
    var self = this;
    this.options = options;
    this.columns = IColumn.buildColumns(this, options.columns);
    this.rawData = data;
    self.editingRow = ko.observable(null);
    self.pageSize = ko.observable(options.pageSize || 10);
    self.pageIndex = ko.observable(0);
    self.templates = {
        cellDisplay: '',
        cellEdit: ''
    };


    this.filteredData = ko.computed(function () {
        return ko.utils.arrayFilter(self.rawData(), function (r) {
            //alert(r.lastName);
            //return r.lastName == 'Trần';
            return true;
        });
    });
    self.maxPageIndex = ko.dependentObservable(function () {
        return Math.ceil(self.filteredData().length / self.pageSize()) - 1;
    });

    this.data = ko.dependentObservable(function () {
        var size = self.pageSize();
        if (self.pageIndex() > self.maxPageIndex())
            self.pageIndex(self.maxPageIndex());
        var start = self.pageIndex() * size;
        return self.filteredData().slice(start, start + size);
    });

    self.pages = ko.dependentObservable(function () {
        var _pages = [];
        for (i = 0; i <= self.maxPageIndex() ; i++) {
            _pages.push({ pageNumber: (i + 1) });
        }
        return _pages;
    });
    self.previousPage = function () {
        if (self.pageIndex() > 0) {
            self.pageIndex(self.pageIndex() - 1);
        }
    };
    self.nextPage = function () {
        if (self.pageIndex() < self.maxPageIndex()) {
            self.pageIndex(self.pageIndex() + 1);
        }
    };
    self.moveToPage = function (index) {
        self.pageIndex(index);
    };


    this.buildTable = function () {
        var $table = $('<table>');

        $table.addClass('table');
        $table.addClass('table-striped');
        $table.addClass('table-hover');
        this.buildHeader($table);
        this.buildBody($table);
        $navigator = this.buildPager();
        $table.append($navigator);
        return $table;
    };

    this.buildHeader = function ($table) {
        this.$thead = $('<thead>');
        $thr = $('<tr>');
        this.$thead.append($thr);
        for (i = 0 ; i < this.columns.length; i++) {
            $thr.append(this.columns[i].renderColumnHeader());
        }
        $table.append(this.$thead);
    };

    this.buildBody = function ($table) {
        $tbody = $('<tbody data-bind="foreach: data">');
        //$tbody.append('<tr data-bind="foreach: $parent.columns, click: $root.rowSelected"><td data-bind="text: $parent[$data.field]"></td></tr>');
        //$trow = $('<tr data-bind="row: $parent.columns">');
        $trow = $('<tr data-bind="row: $parent.columns">');
        //$trow = $('<tr data-bind="foreach: $parent.columns, click: $parent.rowClick.bind($data, $context)">');

        //if ( typeof this.rowClick === 'function')
        //    $tbody.delegate('tr', 'click', this.rowClick);
        if (self.options.childField) {
            $trow.append($('<td data-bind="text: $parent[$parents[1].options.childField]()[$data.field]()">'));
        } else {
            $trow.append($('<td data-bind="column: $parent[$data.field]">'));
            //$trow.append($('<td data-bind="text: $parent[$data.field]">'));
            //$trow.append($('<td data-bind="text: $index()">'));
        }
        $tbody.append($trow);

        $table.append($tbody);
    }

    this.buildPager = function () {
        $tfoot = $('<tfoot>');

        $trow = $('<tr>');
        $tcell = $('<td>').attr('colspan', this.columns.length);
        $trow.append($tcell);
        $tfoot.append($trow);
        $divRow = $('<div class="row">');

        $pageSizeDiv = $('<div class="col-xs-2">');
        $pageSize = $('<select class="form-control"  data-bind="value: $root.pageSize, options: [10,25,50,100]">');
        $pageSizeDiv.append($pageSize);

        $div = $('<div class="col-xs-8">');
        $pagerPrev = $('<ul style="-webkit-padding-start: 0px;margin: 0px;">');
        $pagerPrev.addClass('pagination');
        $previous = $('<li data-bind="css: {disabled: pageIndex() === 0}"><a href="#" data-bind="click: previousPage">&laquo;</a></li>');
        $pagerPrev.append($previous);
        $pages = $('<ul class="pagination" style="-webkit-padding-start: 0px;margin: 0px;" data-bind="foreach: pages">');
        $pages.append('<li data-bind="css: { active: $data.pageNumber === ($root.pageIndex() + 1) }"><a href="#" data-bind="text: $data.pageNumber, click: function() { $root.moveToPage($data.pageNumber-1); }"></a></li>');
        $next = $('<li data-bind="css: {disabled: pageIndex() === maxPageIndex()}"><a href="#" data-bind="click: nextPage">&raquo;</a></li>');
        $pagerNext = $('<ul style="-webkit-padding-start: 0px;margin: 0px;">');
        $pagerNext.addClass('pagination');
        $pagerNext.append($next);
        $div.append($pagerPrev);
        $div.append($pages);
        $div.append($pagerNext);


        $divRow.append('<div style="vertical-align: middle; float: left; margin-top: 5px;">Số dòng</div>');
        $divRow.append($pageSizeDiv);

        $divRow.append($div);
        $tcell.append($divRow);

        return $tfoot;
    }
    this.removeRow = function (data) {
        data.entityAspect.setDeleted();
    };
    this.addRow = function () {

    };
}

ColumnTemplateEngine = function (columnModel) {
    this.makeTemplateSource = function (template) {
        return new ko.templateSources.columnTemplate(template, columnModel);
    };
}

ColumnTemplateEngine.prototype = new ko.nativeTemplateEngine();
ColumnTemplateEngine.prototype.constructor = ColumnTemplateEngine;
ColumnTemplateEngine.instance = new ColumnTemplateEngine();

function IColumn(tableModel, options) {
    this.field = options.field;
    this.text = options.text;
    this.options = options;
    this.table = tableModel;
    this.sortable = options.sortable || false;
    this.templateEngine = new ColumnTemplateEngine(this);
    
    if (options.type)
        this.columnType = options.type;
    else
        this.columnType = IColumn.Type.Text;

    this.renderColumn = function () {

    };
    this.renderColumnHeader = function () {
        var $cell = $('<th data-field="' + this.field + '">').html(this.text);
        if (this.sortable) {
            $cell.click({ column: this }, this.sort);
            $cell.addClass('sortable');
        }

        return $cell;
    };
    this.sort = function (event) {
        var field = event.data.column.field;
        var table = event.data.column.table;
        $cell = $(event.target);
        if ($cell.hasClass('datagrid-sort'))
            $cell = $(event.target).parent();
        table.$thead.find('i.datagrid-sort').remove();
        table.$thead.find('th').removeClass('sorted');
        event.data.column.sortDirection = event.data.column.sortDirection === 'asc' ? 'desc' : 'asc';
        className = (event.data.column.sortDirection === 'asc' ? 'glyphicon glyphicon-chevron-down' : 'glyphicon glyphicon-chevron-up');

        $cell.append($('<i>').addClass(className + ' datagrid-sort'));
        $cell.addClass('sorted');
        if (table.options.childField) {
            table.rawData.sort(function (a, b) {
                return (event.data.column.sortDirection === 'asc' ? 1 : -1) *
                    (a[table.options.childField]()[field]() < b[table.options.childField]()[field]() ? -1 : a[table.options.childField]()[field]() > b[table.options.childField]()[field]() ? 1 : a[table.options.childField]()[field]() == b[table.options.childField]()[field]() ? 0 : 0);
            });
        } else {
            table.rawData.sort(function (a, b) {
                return (event.data.column.sortDirection === 'asc' ? 1 : -1) *
                    (a[field] < b[field] ? -1 : a[field] > b[field] ? 1 : a[field] == b[field] ? 0 : 0);
            });
        }
    }
}

IColumn.Type = {
    Text: 0,
    Date: 1
};

IColumn.buildColumns = function (tableModel, options) {
    var columns = new Array();
    for (i = 0; i < options.length; i++) {
        columns.push(new IColumn(tableModel, options[i]));
    }
    return columns;
};
